package model

import (
	"errors"
	"sync"
)

// ServerList 服务器信息表
type ServerList struct {
	// 子服务器的集合
	servers map[Address]*Server

	// 用于服务器副本列表的信息同步
	// 当 update 为真时，副本列表会在心跳检测前更新到最新状态
	// 方式是获取 addedServers 和 removedServers 的内容，进行副本的增减
	update bool

	// 最近新增的和移除的服务器
	// 副本列表更新状态时会用到，更新完状态会将这两项清空
	addedServers   map[Address]*Server
	removedServers map[Address]*Server

	mutex *sync.RWMutex
}

func NewServerList() *ServerList {
	return &ServerList{
		servers:        make(map[Address]*Server),
		update:         false,
		addedServers:   make(map[Address]*Server),
		removedServers: make(map[Address]*Server),
		mutex:          new(sync.RWMutex),
	}
}

// GetServerByAddr 根据地址获取服务器
func (list *ServerList) GetServerByAddr(addr Address) *Server {
	list.mutex.RLock()
	defer list.mutex.RUnlock()

	return list.servers[addr]
}

// GetServers 获取所有子服务器
func (list *ServerList) GetServers() map[Address]*Server {
	list.mutex.RLock()
	defer list.mutex.RUnlock()

	return list.servers
}

// AddServer 添加新的服务器
func (list *ServerList) AddServer(addr Address, server *Server) error {
	list.mutex.Lock()
	defer list.mutex.Unlock()

	// 检查服务器地址的有效性
	if addr != server.addr {
		return errors.New("server " + server.name + "'s address is not " + string(addr))
	}

	// 防止服务器多次注册
	if list.servers[addr] != nil {
		return errors.New("server " + server.name + " is already in the list")
	}

	// 将该服务器添加到列表中
	list.servers[addr] = server

	// 设置更新标志
	list.update = true
	list.addedServers[addr] = server

	return nil
}

// RemoveServer 移除服务器
func (list *ServerList) RemoveServer(addr Address) (*Server, error) {
	list.mutex.Lock()
	defer list.mutex.Unlock()

	// 确保这个服务器在列表中
	if list.servers[addr] == nil {
		return nil, errors.New("there is no server with address " + string(addr))
	}

	// 将该服务器从列表中移除
	removedServer := list.servers[addr]
	delete(list.servers, addr)

	// 设置更新标志
	list.update = true
	list.removedServers[addr] = removedServer

	return removedServer, nil
}

// MatchServer 用户创建房间时，为其匹配到合适的服务器
func (list *ServerList) MatchServer(load int) (Address, bool) {
	list.mutex.RLock()
	defer list.mutex.RUnlock()

	// 如果当前没有服务器，直接返回
	if len(list.servers) == 0 {
		return "", false
	}

	// 标记当前是否有合适的服务器
	// 当该值为 true 时，在找到另一个合适的服务器时，会进行比较以选出更合适的
	selected := false

	// 当前保存着的目标服务器地址以及占用后它的剩余负载
	// 为了让服务器资源被充分利用，选择的服务器尽可能剩余负载最小
	var targetAddr Address
	var targetSurplus int

	for addr, server := range list.servers {
		// 判断该服务器是否有足够多的可用负载
		// 注意，当这个方法返回为 true 的时候，已经在这个服务器中占用一定负载了
		if !server.AddLoad(load) {
			continue
		}

		// 若当前没找到合适的服务器，将该服务器暂时设置为目标服务器
		if !selected {
			targetAddr = addr
			targetSurplus = server.GetAvailableLoad()
			selected = true
			continue
		}

		// 如果在该服务器上创建会议更能利用服务器资源，就更换目标服务器
		currentSurplus := server.GetAvailableLoad()
		if currentSurplus < targetSurplus {
			// 将原服务器上占用的资源释放
			list.servers[targetAddr].MinusLoad(load)

			// 更换目标服务器
			targetAddr = addr
			targetSurplus = currentSurplus
		}
	}

	return targetAddr, selected
}
